"use strict";

var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var Shutter = (function () {

    "use strict";

    var cache = [];

function $(selector) {
    if (typeof(selector) === "string" && cache[selector]){
        return cache[selector];
    } else if (selector instanceof HTMLElement || selector instanceof HTMLDocument){
        for (var k=0; k<cache.length; k++){
            if (cache[k].elements[0] === selector && cache[k].length === 1){
                return cache[k];
            }
        }
    } else if (selector instanceof Array){
        for(var i=0; i<cache.length; i++){
            var item = cache[i];
            for (var j=0; j<item.elements.length; j++){
                if (item.elements.length !== selector.length || item.elements[j] != selector[j]){
                    break;
                }
                if (j === selector.length - 1){
                    return cache[i];
                }
            }
        }
    } else if (typeof(selector) === "function"){
        selector();
        return;
    }
    var obj = new iQuery(selector);
    cache.push(obj);
    return obj;
}

function iQuery(selector) {

    // if (!selector) return;

    var that = this, EVENTS = createEventsObject();
    that.prefix = ["-webkit-", "-moz-", "-ms-", "-o-"];
    that.selector = null;
    that.elements = [];
    that.typeName = "iQuery";

    if (!selector){

    } else if (selector instanceof Array){
        that.elements = selector;
    } else if (selector instanceof NodeList) {
        that.elements = nodeListToArray(selector);
    } else if (selector instanceof HTMLElement || selector instanceof HTMLDocument) {
        that.elements = [selector];
    } else if (selector.typeName === "iQuery") {
        that.elements = selector.elements;
    } else if (typeof selector === "string"){
        if (/<.*?>/i.test(selector)){
            that.elements = nodeListToArray(parseHTML(selector));
        } else {
            that.selector = selector;
            that.elements = nodeListToArray(document.querySelectorAll(selector));
        }
    }

    Object.defineProperty(that, "length", {
        get: function () {
            return that.elements.length;
        }
    });

    that.elements.forEach(function (el, i) {
        that[i] = el;
    });

    ["top", "left", "right", "bottom", "width", "height"].forEach(function (attr) {
        that[attr] = function (value) {
            if (typeof(value) === "undefined"){
                return getBoundingClientRect()[attr];
            } else {
                that.css(attr, /\d+[a-z]/i.test(value) ? value : value + "px");
                return that;
            }
        }
    });

    this.val = function (value) {
        if (typeof value === "undefined"){
            return that[0].value;
        } else {
            that[0].value = value;
            return that;
        }
    };

    this.attr = function (name, value) {
        if (typeof name === "string") {
            if (typeof(value) === "undefined"){
                if (that.length > 0 && that.elements[0].getAttribute){
                    return that.elements[0].getAttribute(name);
                } else {
                    return "";
                }
            } else {
                that.elements.forEach(function (el) {
                    el.setAttribute(name, value);
                });
                return that;
            }
        } else if (typeof name === "object") {
            for (var key in name){
                (function (k) {
                    that.elements.forEach(function (el) {
                        el.setAttribute(k, value);
                    });
                })(key);
            }
            return that;
        }
    };

    this.css = function (name, value, hasPrefix) {
        if (typeof name === "string"){
            if (typeof(value) === "undefined"){
                return that.elements[0].style[name];
            } else {
                setStyle(name, value, hasPrefix);
                return that;
            }
        } else if (typeof name === "object") {
            for (var key in name){
                setStyle(key, name[key], hasPrefix);
            }
            return that;
        }
    };

    this.show = function () {
        setStyle("display", "");
        return that;
    };

    this.hide = function () {
        setStyle("display", "none");
        return that;
    };

    this.addClass = function (classNames) {
        that.elements.forEach(function (el) {
            classNames.split(" ").forEach(function (className) {
                className && el.classList.add(className);
            })
        });
        return that;
    };

    this.removeClass = function (classNames) {
        that.elements.forEach(function (el) {
            classNames.split(" ").forEach(function (className) {
                el.classList.remove(className);
            });
        });
        return that;
    };

    this.hasClass = function (className) {
        return that.elements[0].classList.contains(className);
    };

    this.toggleClass = function (className) {
        that.hasClass(className) ? that.removeClass(className) : that.addClass(className);
        return this;
    };

    this.html = function (str) {
        if (typeof(str) === "undefined"){
            return that.elements[0].innerHTML;
        } else {
            that.elements.forEach(function (el) {
                el.innerHTML = str;
            });
        }
        return that;
    };

    this.append = function (html) {
        that.elements.forEach(function (el) {
            if (html instanceof HTMLElement){
                el.appendChild(html);
            } else if (html.typeName === "iQuery"){
                html.elements.forEach(function (child) {
                    el.appendChild(child);
                });
            } else if (typeof html === "string"){
                var nodes = parseHTML(html);
                for (var i=0; i<nodes.length; i++){
                    el.appendChild(nodes[i]);
                }
            }
        });
        return that;
    };

    this.remove = function () {
        that.elements.forEach(function (el) {
            if (el.remove){
                el.remove();
            }
        });
    };

    this.find = function (selector) {
        var list = [];
        that.elements.forEach(function (el) {
            $(el.querySelectorAll(selector)).each(function (child) {
                list.push(child);
            });
        });
        return $(list);
    };

    this.parent = function () {
        var parents = [];
        that.elements.forEach(function (el) {
            if (el.parentElement){
                parents.push(el.parentElement);
            }
        });
        return $(parents);
    };

    this.parents = function () {
        var parents = [];
        that.elements.forEach(function (el) {
            while (el.parentElement){
                parents.push(el.parentElement);
                el = el.parentElement;
            }
        });
        return parents;
    };

    this.closest = function (selector) {
        var parents = [], matched = false;
        that.elements.forEach(function (el) {
            var els = $(document).find(selector);
            while (el.parentElement){
                for (var i=0; i<els.length; i++){
                    if (els[i] === el.parentElement){
                        parents.push(els[i]);
                        matched = true;
                        break;
                    }
                }
                if (matched) break;
                el = el.parentElement;
            }
        });
        return $(parents);
    };

    this.children = function (selector) {
        return $(that.elements.map(function (el) {
            return el.children;
        }));
    };

    this.eq = function (index) {
        return $(that.elements[index]);
    };

    this.next = function () {
        if (that.elements.length > 0 && that.elements[0].nextElementSibling){
            return $(that.elements[0].nextElementSibling);
        }
        return null;
    };

    this.prev = function () {
        if (that.elements.length > 0 && that.elements[0].previousElementSibling){
            return $(that.elements[0].previousElementSibling);
        }
        return null;
    };

    this.each = function (callback) {
        that.elements.forEach(callback);
    };

    this.reverse = function () {
        that.elements.reverse();
        return that;
    };

    this.indexOf = function (element) {
        for (var i=0; i<that.length; i++){
            if (that[i] === element){
                return i;
            }
        }
        return -1;
    };

    this.on = function (eventName, selector, handle, useCapture) {
        var IsDelegate = true;
        if (typeof selector === "function") {
            IsDelegate = false;
            handle = selector;
        }
        that.elements.forEach(function (el) {
            var delegate = IsDelegate === false ? handle : function (e) {
                var children = el.querySelectorAll(selector);
                for (var i=0; i<children.length; i++){
                    var child = children[i];
                    if (child === e.target) {
                        handle.call(child, e);
                        break;
                    }
                }
            };
            EVENTS.push({
                target: el,
                event: eventName,
                selector: selector,
                handle: delegate
            });
            eventName.split(" ").forEach(function (name) {
                el.addEventListener(name, delegate, useCapture);
            });
        });
        return this;
    };

    this.off = function (eventName, handle, useCapture) {
        that.elements.forEach(function (el) {
            if (!handle){
                var evt = EVENTS.find(el, eventName);
                if (evt) handle = evt.handle;
            }
            eventName.split(" ").forEach(function (name) {
                el.removeEventListener(name, handle, useCapture);
            })
        });
        return this;
    };

    this.one = function (eventName, handle, useCapture) {
        that.on(eventName, function (e) {
            that.off(eventName);
            handle.call(this, e);
        }, useCapture);
        return this;
    };

    this.once = function (eventName, handle, useCapture, timespan=300) {
        var locker = 0;
        var date1 = new Date();
        that.on(eventName, function (e) {
            var date2 = new Date();
            if (locker === 0 && date2 - date1 > timespan){
                locker++;
                date1 = date2;
                try{
                    handle.call(this, e);
                } finally {
                    locker = 0;
                }
            }
        }, useCapture);
    };

    this.trigger = function (eventName) {
        var args = arguments;
        that.elements.forEach(function (el) {
            var evt = EVENTS.find(el, eventName);
            if (evt){
                evt.handle.apply(el, Array.prototype.slice.call(args, 1));
            } else {
                if (!EVENTS[eventName])
                    EVENTS[eventName] = new Event(eventName);
                el.dispatchEvent(EVENTS[eventName]);
            }
        });
        return that;
    };

    function setStyle(name, value, hasPrefix) {
        that.elements.forEach(function (el) {
            if (["top", "left", "right", "bottom", "width", "height"].indexOf(name) > -1 && !/[a-z]/i.test(value)){
                value += "px";
            }
            hasPrefix && setPrefixStyle(el, name, value);
            el.style[name] = value;
        })
    }

    function setPrefixStyle(el, name, value) {
        that.prefix.forEach(function (pre) {
            el.style[pre + name] = value;
        })
    }

    function getBoundingClientRect() {
        if (that.elements[0]){
            return that.elements[0].getBoundingClientRect();
        }
        return {};
    }

    function createEventsObject() {
        var events = [];
        events.find = function (target, eventName) {
            for (var i=0; i<this.length; i++){
                var evt = EVENTS[i];
                if (evt.target === target && evt.event === eventName){
                    return evt;
                }
            }
        };
        return events;
    }

    function nodeListToArray(nodeList) {
        return Array.prototype.slice.call(nodeList, 0);
    }

    function parseHTML(str) {
        var wrap = document.createElement("div");
        wrap.innerHTML = str;
        return wrap.children;
    }

}

function _extend(obj) {
    for (var i=1; i<arguments.length; i++){
        var ext = arguments[i];
        for (var key in ext){
            if (obj[key] && typeof ext[key] === "object"){
                _extend(obj[key], ext[key]);
            } else if (typeof ext[key] !== "undefined"){
                obj[key] = ext[key];
            }
        }
    }
    return obj;
}

$.extend = _extend;

    //切换效果
    var Effects = List({
        index: 0,
        animation: 0,
        interval: 100
    }, {
        index: 1,
        animation: 1,
        interval: 80
    }, {
        index: 2,
        animation: 2,
        interval: 200
    });
    Object.defineProperty(Effects, "random", {
        get: function get() {
            //不重复随机效果
            var randomEffect = null;
            var IsRepeat = true;
            while (IsRepeat) {
                randomEffect = Effects[Math.floor(Math.random() * this.length)];
                IsRepeat = this.prevEffect === randomEffect;
            }
            this.prevEffect = randomEffect;
            return randomEffect;
        }
    });

    //动画方法
    var Animations = List({
        index: 0,
        method: function fadeOut(shutter, current) {
            $(current).addClass("fadeOut animated");
        }
    }, {
        index: 1,
        promise: function promise(shutter, active, next) {
            var isEnd = false;
            return new Promise(function (resolve, reject) {
                var $active = $(active);
                var $next = $(next);
                var $activeList = $active.find(".shutter-slide-cell div");
                var $nextList = $next.find(".shutter-slide-cell div");
                var delay = 0;
                var animateClass = "easeOutBounce1";
                for (var i = 0; i < $activeList.length; i++) {

                    if (i > 0 && i % shutter.conf.x === 0) delay += 0.3;
                    $activeList.eq(i).css("animation-delay", delay + "s", true);
                    $nextList.eq(i).css("animation-delay", delay + "s", true);
                    delay += shutter.effect.interval / 1000;
                }
                $activeList.eq($activeList.length - 1).one("webkitAnimationEnd animationend", function () {
                    if (isEnd) return;
                    isEnd = true;
                    $activeList.css("animation-delay", "", true);
                    $nextList.css("animation-delay", "", true);
                    $active.parent().removeClass(animateClass);
                    resolve();
                });
                $active.parent().addClass(animateClass);
            });
        }
    }, {
        index: 2,
        promise: function promise(shutter, active, next) {
            var isEnd = false;
            return new Promise(function (resolve, reject) {
                var $active = $(active);
                var $next = $(next);
                var index = 0;
                var delay = 0;
                var animateClass = "slideIn1";
                for (var i = 0; i < shutter.conf.x; i++) {
                    delay = shutter.effect.interval * i / 1000 + "s";
                    for (var j = 0; j < shutter.conf.y; j++) {
                        index = i + j * shutter.conf.x + 1;
                        $active.find(".shutter-slide-cell:nth-of-type(" + index + ") div").css("animation-delay", delay, true);
                        $next.find(".shutter-slide-cell:nth-of-type(" + index + ") div").css("animation-delay", delay, true);
                    }
                }
                $active.find(".shutter-slide-cell:last-child").one("webkitAnimationEnd animationend", function () {
                    if (isEnd) return;
                    isEnd = true;
                    $active.find(".shutter-slide-cell div").css("animation-delay", "", true);
                    $next.find(".shutter-slide-cell div").css("animation-delay", "", true);
                    $active.parent().removeClass(animateClass);
                    resolve();
                });
                $active.parent().addClass(animateClass);
            });
        }
    }, {
        index: 3,
        promise: function promise(shutter, active, next) {
            var isEnd = false;
            return new Promise(function (resolve, reject) {
                var $active = $(active);
                var $next = $(next);
                var index = 0;
                var delay = 0;
                var animateClass = "slideIn1";
                var list = [];
                var delay = 0;
                for (var _i = 0; _i < shutter.conf.x * shutter.conf.y; _i++) {
                    list[_i] = _i;
                }
                list.sort(function () {
                    return Math.random() - 0.5;
                }); // 打乱元素索引顺序
                list.forEach(function (value, index, obj) {
                    $active.find(".shutter-slide-cell:nth-of-type(" + index + ") div").css("animation-delay", delay, true);
                    $next.find(".shutter-slide-cell:nth-of-type(" + index + ") div").css("animation-delay", delay, true);
                });
                for (var i = 0; i < shutter.conf.x; i++) {
                    delay = shutter.effect.interval * i / 1000 + "s";
                    for (var j = 0; j < shutter.conf.y; j++) {
                        index = i + j * shutter.conf.x + 1;
                        $active.find(".shutter-slide-cell:nth-of-type(" + index + ") div").css("animation-delay", delay, true);
                        $next.find(".shutter-slide-cell:nth-of-type(" + index + ") div").css("animation-delay", delay, true);
                    }
                }
                $active.find(".shutter-slide-cell:last-child").one("webkitAnimationEnd animationend", function () {
                    if (isEnd) return;
                    isEnd = true;
                    $active.find(".shutter-slide-cell div").css("animation-delay", "", true);
                    $next.find(".shutter-slide-cell div").css("animation-delay", "", true);
                    $active.parent().removeClass(animateClass);
                    resolve();
                });
                $active.parent().addClass(animateClass);
            });
        }
    });

    //特殊数组
    function List() {
        var list = Array.prototype.slice.call(arguments, 0);
        list.add = function (index, value) {
            if (!this[index]) {
                this[index] = value;
            } else {
                throw new Error("shutter: index already exists in the list.");
            }
        };
        return list;
    }

    //自定义异常类型
    function shutterError(message) {
        this.name = "shutterError";
        this.message = message || "unknown error";
        this.stack = new Error().stack;
    }
    shutterError.prototype = Object.create(Error.prototype);
    shutterError.prototype.constructor = shutterError;

    //插件主体

    var Shutter = (function () {
        _createClass(Shutter, [{
            key: "slideCount",
            get: function get() {
                return this.$container.find(".shutter-slide").length;
            }
        }, {
            key: "status",
            get: function get() {
                return this._status || 0;
            },
            set: function set(value) {
                this._status = value;
            }
        }], [{
            key: "effects",
            get: function get() {
                return Effects;
            }
        }, {
            key: "animations",
            get: function get() {
                return Animations;
            }
        }]);

        function Shutter(selector, options) {
            _classCallCheck(this, Shutter);

            this.selector = selector;
            this.defaults = {
                selector: selector,
                x: 5,
                y: 3,
                width: "auto",
                height: "auto",
                align: "middle",
                image: {
                    size: "contain",
                    position: "center center"
                },
                index: 0,
                effect: [0, 1, 2],
                autoplay: 3000,
                onSlideChangeStart: function onSlideChangeStart() {},
                onSlideChangeEnd: function onSlideChangeEnd() {}
            };
            this.conf = $.extend({}, this.defaults, options);
            this.init();
        }

        _createClass(Shutter, [{
            key: "init",
            value: function init() {
                var that = this;
                that.index = that.conf.index;
                that.effectIndex = 0;
                that.$container = $(that.selector);
                that.containerWidth = that.$container.width();
                that.containerHeight = that.$container.height();
                that.cellWidth = that.containerWidth / that.conf.x; //数值为小数点会有缝隙
                that.cellHeight = that.containerHeight / that.conf.y; //数值为小数点会有缝隙
                that.$container.find(".shutter-slide").each(function (slide, index) {
                    var $slide = $(slide);
                    var $image = $slide.find("img");
                    var src = $image.attr("src");
                    var cellCount = that.conf.x * that.conf.y;
                    var rowNumber = 0,
                        colNumber = 0;
                    var isLoaded = false;
                    $image.on("load", function () {
                        if (isLoaded) return;
                        isLoaded = true;
                        var bg = that.getBackground($image);
                        for (var i = 0; i < cellCount; i++) {
                            var currentRow = Math.floor(i / that.conf.x);
                            if (currentRow > rowNumber) {
                                colNumber = 0;
                                rowNumber = currentRow;
                            }
                            var cellTop = rowNumber * that.cellHeight;
                            var cellLeft = colNumber * that.cellWidth;
                            var bgTop = -bg.startTop + cellTop;
                            var bgLeft = -bg.startLeft + cellLeft;
                            $slide.append("\n                            <div class=\"shutter-slide-cell\" style=\"width: " + that.cellWidth + "px; height: " + that.cellHeight + "px; top: " + cellTop + "px; left: " + cellLeft + "px;\">\n                                <div style=\"background: url(" + src + ") " + (0 - bgLeft) + "px " + (0 - bgTop) + "px no-repeat; background-size: " + bg.size + ";\"></div>\n                            </div>\n                        ");
                            colNumber++;
                        }
                    });
                    $image.attr("src", src);
                });
                that.$container.find(".shutter-slide.active").removeClass("active");
                that.$container.find(".shutter-slide:nth-child(" + (that.index + 1) + ")").addClass("active");
                $(document).on("click", "[data-toggle-slide='prev']", function () {
                    that.slidePrev();
                });
                $(document).on("click", "[data-toggle-slide='next']", function () {
                    that.slideNext();
                });
                that.conf.autoplay && that.autoplay();
            }
        }, {
            key: "getBackground",
            value: function getBackground($image) {
                var that = this;
                var bg = { size: null, startTop: 0, startLeft: 0 };
                $image.width(that.containerWidth);
                // var imageWidth = $image.width();
                var imageHeight = $image.height();
                if (that.conf.image.size === "contain") {
                    if (imageHeight > that.containerHeight) {
                        $image.css({ width: "auto", height: that.containerHeight });
                        bg.size = "auto " + that.containerHeight + "px";
                        bg.startLeft = (that.containerWidth - $image.width()) / 2;
                    } else {
                        bg.size = that.containerWidth + "px auto";
                        bg.startTop = (that.containerHeight - imageHeight) / 2;
                    }
                } else if (that.conf.image.size === "cover") {
                    if (imageHeight > that.containerHeight) {
                        bg.size = that.containerWidth + "px auto";
                        bg.startTop = (that.containerHeight - imageHeight) / 2;
                    } else {
                        $image.css({ width: "auto", height: that.containerHeight });
                        bg.size = "auto " + that.containerHeight + "px";
                        bg.startLeft = (that.containerWidth - $image.width()) / 2;
                    }
                } else {
                    bg.size = that.conf.image.size.replace(/\d+%/g, function (matched, index, obj) {
                        var value = parseFloat(/\d+/.exec(matched));
                        if (index === 0) {
                            value = value / 100 * that.containerWidth;
                            $image.width(value);
                        } else {
                            value = value / 100 * that.containerHeight;
                            $image.height(value);
                        }
                        return value + "px";
                    });
                }
                if (that.conf.image.position) {
                    var posArr = that.conf.image.position.replace(/top|left|right|bottom|center/gi, function (matched, index, obj) {
                        switch (matched) {
                            case "top":
                                return 0;
                            case "left":
                                return 0;
                            case "right":
                                return that.containerWidth - $image.width();
                            case "bottom":
                                return that.containerHeight - $image.height();
                        }
                        if (matched === "center") {
                            var value = index === 0 ? that.containerWidth - $image.width() : that.containerHeight - $image.height();
                            return value / 2;
                        }
                    });
                    posArr = posArr.replace(/\d+%/g, function (matched, index, obj) {
                        var value = index === 0 ? that.containerWidth : that.containerHeight;
                        return parseFloat(/\d+/.exec(matched)) / 100 * value + "px";
                    }).split(" ");
                    bg.startLeft = parseFloat(posArr[0]);
                    bg.startTop = parseFloat(posArr[1]);
                }
                return bg;
            }
        }, {
            key: "slideTo",
            value: function slideTo(index, callback) {
                if (this.status > 0) return;
                this.status = 1;

                // if (index >= this.slideCount) index = 0;
                // if (index < 0) index = this.slideCount - 1;
                this.$container.find(".shutter-slide:nth-child(" + (index + 1) + ")").addClass("next");

                var that = this;
                var cellIndex = 0;
                var intervalId = 0;
                var effectIndex = that.conf.effect instanceof Array ? that.conf.effect[that.effectIndex] : that.conf.effect;
                var effect = that.effect = Effects[effectIndex];
                var cellCount = this.conf.x * this.conf.y;
                var $slide = that.$container.find(".shutter-slide.active");
                var $next = that.$container.find(".shutter-slide.next");
                var animation = Animations[effect.animation];
                var promise = null;
                if (animation.promise) {
                    promise = animation.promise(that, $slide[0], $next[0]);
                } else {
                    promise = new Promise(function (resolve, reject) {
                        intervalId = setInterval(function () {
                            var $cell = $slide.find(".shutter-slide-cell:nth-of-type(" + (cellIndex + 1) + ")");
                            animation = Animations[effect.animation] || animation;
                            animation.method(that, $cell[0], cellIndex);
                            if (cellIndex === cellCount - 1) {
                                $cell.one("webkitAnimationEnd animationend", function () {
                                    animation.clear && animation.clear(that);
                                    $slide.find(".shutter-slide-cell").attr("class", "shutter-slide-cell");
                                    resolve();
                                });
                                clearInterval(intervalId);
                            }
                            cellIndex++;
                        }, effect.interval);
                    });
                }

                that.conf.onSlideChangeStart.call($slide[0], that, $next[0]);
                promise.then(function () {
                    $slide.removeClass("active");
                    $next.removeClass("next").addClass("active");
                    that.index = index;
                    if (that.conf.effect instanceof Array) {
                        that.effectIndex >= that.conf.effect.length - 1 ? that.effectIndex = 0 : that.effectIndex++;
                    }
                    that.status = 0;
                    callback && callback();
                    that.conf.onSlideChangeEnd.call($slide[0], that, $next[0]);
                    that.conf.autoplay && that.autoplay();
                });
            }
        }, {
            key: "slidePrev",
            value: function slidePrev(callback) {
                var that = this;
                that.slideTo(that.index - 1 < 0 ? that.slideCount - 1 : that.index - 1, callback);
            }
        }, {
            key: "slideNext",
            value: function slideNext(callback) {
                var that = this;
                that.slideTo(that.index + 1 >= that.slideCount ? 0 : that.index + 1, callback);
            }
        }, {
            key: "autoplay",
            value: function autoplay() {
                var that = this;
                setTimeout(function () {
                    that.slideNext(function () {
                        that.autoplay();
                    });
                }, that.conf.autoplay);
            }
        }]);

        return Shutter;
    })();

    return Shutter;
})();